package com.pz.minioproxy.service;

import com.pz.gulimall.common.exception.RRException;
import com.pz.minioproxy.constant.BigFileConstant;
import io.minio.*;
import io.minio.messages.Item;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicLong;

@Service
@Slf4j
public class BigFileDownloadService {
    @Resource
    private MinioClient minioClient;
    @Resource
    private ThreadPoolExecutor executor;
    @Resource
    private MinIoService minIoService;
    @Resource
    private BigFileUploadService bigFileUploadService;
    private static final String bucketName = BigFileConstant.bucketName;

    public String getFileName(String md5) {
        return md5+getFileSuffix(md5);
    }

    /**
     * 获取存储在minio服务器上的大文件后缀
     * @param md5 大文件md5值
     * @return 大文件后缀 {.mp4, .jpg, ...}
     */
    public String getFileSuffix(String md5) {
        String blobPathPrefix = BigFileConstant.getBlobPathPrefix(md5);
        try {
            Iterable<Result<Item>> objectsIterable = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(bucketName)
                            .prefix(blobPathPrefix)
                            .maxKeys(2)
                            .recursive(true).build());
            List<String> ans = new ArrayList<>();
            for (Result<Item> itemResult : objectsIterable) {
                Item item = itemResult.get();
                ans.add(item.objectName());
            }
            if(ans.size() < 2){
                return "";
            } else {
                String str = ans.get(1);
                return str.substring(str.lastIndexOf("."));
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RRException("获取大文件后缀异常-"+e.getMessage());
        }
    }

    /**
     * 计算大文件大小
     * @param md5 文件md5
     * @return 文件大小 B
     */
    private long calculateBigFileSize(String md5) {
        String filePrefix = BigFileConstant.getBlobPathPrefix(md5);
        List<String> objNames = minIoService.listObjets(bucketName, filePrefix);
        AtomicLong ans = new AtomicLong();
        List<CompletableFuture<Void>> tasks = new ArrayList<>();
        objNames.forEach(obj -> {
            CompletableFuture<Void> task = CompletableFuture.runAsync(() -> {
                StatObjectResponse objectInfo = minIoService.getObjectInfo(bucketName, obj);
                ans.addAndGet(objectInfo.size());
            }, executor);
            tasks.add(task);
        });
        CompletableFuture<Void> combine = CompletableFuture.allOf(tasks.toArray(new CompletableFuture[0]));
        try {
            combine.get();
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }
        return ans.get();
    }

    /**
     * 合并大文件的分块到一个输出流
     * @param md5 大文件md值
     * @param maxBlobSize 分块文件的最大大小
     * @param outputStream 写入的目标输出流
     */
    public void mergeBlobToBigFile(String md5, long maxBlobSize, OutputStream outputStream) {
        String bigFileObject = BigFileConstant.getBlobPathPrefix(md5);
        log.info("begin to merge {} ...", bigFileObject);
        long size = calculateBigFileSize(md5);
        long blobNum = (long) Math.ceil(size *1.0 / maxBlobSize);
        for (int i = 0; i < blobNum; i++) {
            String blobObject = BigFileConstant.getBlobObjectName(md5, i, getFileSuffix(md5));
            log.info("merger blob{}/{} to {}", i, blobNum, bigFileObject);

            try(InputStream is = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(blobObject)
                            .build())) {
                byte[] buffer = new byte[1024*100];
                int len;
                while ((len = is.read(buffer)) != -1){
                    outputStream.write(buffer, 0, len);
                }
            }catch (Exception e){
                e.printStackTrace();
                throw new RRException("获取 minio 文件流异常-"+e.getMessage());
            }

        }
        log.info("{} merger completed! ", bigFileObject);
    }

    /**
     * 获取大文件各个分块的 minion下载连接
     * @param md5 文件MD5值
     * @return 分块文件下载连接（按照分块先后排序）
     */
    public List<String> getBigFileBlobLinks(String md5) {
        String blobPathPrefix = BigFileConstant.getBlobPathPrefix(md5);
        List<String> listObjets = minIoService.listObjets(bucketName, blobPathPrefix);
        if(listObjets.size() > 1 && bigFileUploadService.isBigFileWholeExist(md5)) {
            listObjets.remove(0);
            listObjets.sort((o1, o2) -> {
                long index1 = Long.parseLong(o1.split("-")[1].split("\\.")[0]);
                long index2 = Long.parseLong(o2.split("-")[1].split("\\.")[0]);
                return (int) (index1-index2);
            });
            List<String> links = new ArrayList<>();
            listObjets.forEach(obj -> {
                links.add(minIoService.getObjectUrl(bucketName, obj));
            });
            return links;
        } else {
            return new ArrayList<>();
        }
    }


}
